package com.san.dissertation.annotation;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.annotation.Id;
import org.springframework.data.mapping.model.MappingException;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener;
import org.springframework.util.ReflectionUtils;

public class CascadeMongoEventListener extends AbstractMongoEventListener {
	@Autowired
	private MongoOperations mongoOperations;

	@Override
	public void onBeforeConvert(final Object source) {

		recur(source);
	}

	public void recur(final Object source) {

		ReflectionUtils.doWithFields(source.getClass(),
				new ReflectionUtils.FieldCallback() {
					public void doWith(Field field)
							throws IllegalArgumentException,
							IllegalAccessException {
						ReflectionUtils.makeAccessible(field);

						if (field.isAnnotationPresent(DBRef.class)
								&& field.isAnnotationPresent(CascadeSave.class)) {
							final Object fieldValue = field.get(source);

							DbRefFieldCallback callback = new DbRefFieldCallback();

							Type type = field.getGenericType();

							System.out.println(field.getGenericType());

							if (type instanceof ParameterizedType) {

								ParameterizedType pType = (ParameterizedType) type;
								Type[] arr = pType.getActualTypeArguments();

								Class<?> clzz = (Class<?>) arr[0];

								if (fieldValue instanceof Collection<?>) {
									
									for(Object item:(Collection)fieldValue){
										
										mongoOperations.save(item);
									}

								}

							}

							/*
							 * if (Collection.class.isAssignableFrom(field
							 * .getType())) {
							 * 
							 * System.out.println(field.get);
							 * 
							 * }
							 */else {

								ReflectionUtils.doWithFields(
										fieldValue.getClass(), callback);

								if (!callback.isIdFound()) {
									throw new MappingException(
											"Cannot perform cascade save on child object without id set");
								}

								mongoOperations.save(fieldValue);
							}
						}

					}

				});
	}

	private static class DbRefFieldCallback implements
			ReflectionUtils.FieldCallback {
		private boolean idFound;

		public void doWith(Field field) throws IllegalArgumentException,
				IllegalAccessException {
			ReflectionUtils.makeAccessible(field);

			if (field.isAnnotationPresent(Id.class)) {
				idFound = true;
			}
		}

		public boolean isIdFound() {
			return idFound;
		}
	}
}